<?php

namespace App\Http\Controllers\Api\Orders;

use App\Http\Controllers\Controller;
use App\Http\Requests\Train\Order\RequiredOrderSnRequest;
use App\Jobs\Pay\ProcessWechatOrderPayCallback;
use App\Jobs\Pay\ProcessWechatRefundCallback;
use App\Models\Common\Channel\Repositories\Interfaces\ChannelRepositoryInterface;
use App\Models\Plane\PlaneOrders\Repositories\Interfaces\PlaneOrderRepositoryInterface;
use App\Models\Task\SnapTasks\Repositories\Interfaces\SnapTaskRepositoryInterface;
use App\Models\Task\SnapTasks\Repositories\SnapTaskAttemptUpgradePaidHighLevel;
use App\Models\Train\TrainOrderPassengers\TrainOrderPassenger;
use App\Models\Train\TrainOrders\Repositories\Interfaces\TrainOrdersRepositoryInterface;
use App\Models\Trip\OrderPay\Repositories\Interfaces\OrderPayRepositoryInterface;
use App\Models\Trip\RefundNeedPay\RefundNeedPay;
use App\Models\Trip\SnapTaskUpgradePay\SnapTaskUpgradePay;
use App\Models\User\Repositories\Interfaces\UserRepositoryInterface;
use App\Trip\Enums\Plane\PlaneProviders;
use App\Trip\Passengers\OrderSubPassenger;
use App\Trip\Payment\PaymentAttach;
use App\Trip\Plane\Providers\BookOf51\Book51;
use App\Trip\Train\Order\Impl\PiaoChiOrderPassengerRefundImpl;
use App\Trip\Wechat\AppId;
use App\Trip\Wechat\Pay\PayRes\PayResult;
use App\Trip\Wechat\Pay\RefundRes\RefundResult;
use App\Trip\Wechat\Pay\WechatPay;
use App\Trip\Wechat\Traits\getWechatPayInfoTrait;
use Carbon\Carbon;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

class PaymentController extends Controller
{
    use getWechatPayInfoTrait;

    private TrainOrdersRepositoryInterface $trainOrderRepo;
    private UserRepositoryInterface        $userRepo;
    private OrderPayRepositoryInterface    $orderPayRepo;
    private WechatPay                      $wechatPay;
    private SnapTaskRepositoryInterface    $snapTaskRepository;
    private string                         $appId;
    private PlaneOrderRepositoryInterface  $planeOrderRepo;

    /**
     * @throws InvalidArgumentException
     */
    public function __construct(
        TrainOrdersRepositoryInterface $ordersRepository,
        PlaneOrderRepositoryInterface  $planeOrdersRepository,
        OrderPayRepositoryInterface    $orderPayRepository,
        UserRepositoryInterface        $userRepository,
        SnapTaskRepositoryInterface    $snapTaskRepository
    )
    {
        $this->trainOrderRepo     = $ordersRepository;
        $this->planeOrderRepo     = $planeOrdersRepository;
        $this->orderPayRepo       = $orderPayRepository;
        $this->userRepo           = $userRepository;
        $this->snapTaskRepository = $snapTaskRepository;
        $this->appId              = AppId::getAppId();
        $this->wechatPay          = new WechatPay(['appid' => $this->appId]);
    }

    /**
     */
    public function callback($type, Request $request)
    {
        Log::info("pay callback before");
        Log::info($request->all());
        if ($type == 'wechat') {
            $app = $this->wechatPay->getApplication();
            // $app 为你实例化的支付对象，此处省略实例化步骤
            $server = $app->getServer();
            // 处理支付结果事件
            $server->handlePaid(function ($message) {
                Log::info("handlePaid start");
                Log::info($message);
                ProcessWechatOrderPayCallback::dispatch(new PayResult($message))->onQueue('{pay_callback}');
                Log::info("handlePaid end");
            });

            // 处理退款结果事件
            $server->handleRefunded(function ($message) {
                Log::info("handleRefund start");
                Log::info($message);
                ProcessWechatRefundCallback::dispatch(new RefundResult($message))->onQueue('{pay_callback}');
                Log::info("handleRefund end");
            });

            return $server->serve();
        }
        Log::info("pay callback after");
    }

    /**
     * 获取支付信息
     *
     * @param RequiredOrderSnRequest $request
     *
     * @return Application|ResponseFactory|Response
     * @throws InvalidArgumentException
     * @throws Exception
     */
    public function pay_info(RequiredOrderSnRequest $request)
    {

        $order_info = $this->trainOrderRepo->getOrderInfo($request->input('order_sn'));
        if (!$order_info || $order_info->getUserId() != auth()->id()) {
            return $this->error(404, "订单不存在");
        }
        Log::info("请求订单支付：{$order_info->getOrderSn()}");
        if ($order_info->isCancel()) {
            return $this->error(1, "订单已取消");
        }


        if (!$order_info->canPay()) {
            return $this->error(1, "订单不可支付");
        }

        // 获取支付信息
        $channelRepo = app(ChannelRepositoryInterface::class);
        $pay_info    = $this->orderPayRepo->createTrainOrderPayInfo($order_info, $this->wechatPay,
            $channelRepo->findChannelByAppId($this->appId));

        // 暂时不考虑单独支付
        $out_trade_no = $pay_info->getPaySn();
        $description  = $pay_info->getPayTitle();
        $time_expire  = Carbon::parse($order_info->getMaxPayAt())->toRfc3339String();

        /**
         * 构造 attach 返回用于判别 发起支付的 级别 如果是飞机 则飞机可以独立支付
         *
         * @var $attach string type =>trip_type,os =>order_sn
         */
        $attach                   = new PaymentAttach([
            'type'     => PaymentAttach::PAY_TYPE_ORDER,  //下单
            'order_sn' => $order_info->getOrderSn(),
        ]);
        $payData                  = [
            "out_trade_no" => $out_trade_no,
            "description"  => $description,
            "time_expire"  => $time_expire,
            "order_amount" => $pay_info->getPayAmount(),
            "attach"       => $attach,
        ];
        $pay_info                 = $this->get_pay_info($payData);
        $pay_info['order_amount'] = $payData['order_amount'];
        return $this->success('', compact('pay_info'));
    }

    /**
     * @throws InvalidArgumentException
     * @throws GuzzleException
     * @throws Exception
     */
    public function plane_pay_info(RequiredOrderSnRequest $request)
    {
        $service    = new Book51();
        $order_info = $this->planeOrderRepo->getOrderInfo($request->input('order_sn'));
        if (!$order_info || $order_info->getUserId() != auth()->id()) {
            return $this->error(404, "订单不存在");
        }

        Log::info("飞机票请求订单支付：{$order_info->getOrderSn()}");
        if ($order_info->isCancel()) {
            return $this->error(1, "订单已取消");
        }


        // 支付前 校验 如果订单费改签订单  支付前验证
        $out_order_id = $order_info->getOutOrderId();
        if ($order_info->isNotChange()) {
            $order_service     = PlaneProviders::from($order_info->order_service)->getPlaneOrderStrategy();
            $service_check_res = $order_service->before_pay_validate([
                'out_order_id' => $out_order_id,
            ]);

            if (!$service_check_res->canPay()) {
                return $this->error(2, "该订单不可支付,请检查订单状态");
            }
        }

        if (!$order_info->canPay()) {
            return $this->error(1, "该订单不可支付");
        }

        // 获取支付信息
        $channelRepo = app(ChannelRepositoryInterface::class);
        $pay_info    = $this->orderPayRepo->createPlaneOrderPayInfo($order_info, $this->wechatPay,
            $channelRepo->findChannelByAppId($this->appId));


        // 暂时不考虑单独支付
        $out_trade_no = $pay_info->getPaySn();
        $description  = $pay_info->getPayTitle();
        $time_expire  = Carbon::parse($order_info->getMaxPayAt())->toRfc3339String();

        /**
         * 构造 attach 返回用于判别 发起支付的 级别 如果是飞机 则飞机可以独立支付
         *
         * @var $attach string type =>trip_type,os =>order_sn
         */
        $attach                   = new PaymentAttach([
            'type'     => PaymentAttach::PAY_TYPE_ORDER,  //下单
            'order_sn' => $order_info->getOrderSn(),
        ]);
        $payData                  = [
            "out_trade_no" => $out_trade_no,
            "description"  => $description,
            "time_expire"  => $time_expire,
            "order_amount" => $pay_info->getPayAmount(),
            "attach"       => $attach,
        ];
        $pay_info                 = $this->get_pay_info($payData);
        $pay_info['order_amount'] = $payData['order_amount'];
        return $this->success('', compact('pay_info', 'out_order_id'));
    }

    public function snap_task_pay_info(Request $request)
    {
        $order_sn = $request->input('order_sn');
        $task_id  = $request->input('task_id');
        if (!$order_sn && !$task_id) {
            return $this->error(1, "参数错误");
        }
        $order_info = null;
        $task_info  = null;
        if ($order_sn) {
            $order_info = $this->trainOrderRepo->getOrderInfo($request->input('order_sn'));
            $task_info  = $this->snapTaskRepository->findOneByTaskId($order_info->getTaskId());
        } else if ($task_id) {
            $task_info  = $this->snapTaskRepository->findOneByTaskId($task_id);
            $order_info = $task_info->getTrainOrderInfo();
        }


        if (!$order_info || $order_info->getUserId() != auth()->id()) {
            return $this->error(404, "订单不存在");
        }

        if (!$task_info || $task_info->getUserId() != auth()->id()) {
            return $this->error(404, "任务不存在");
        }

        /**
         * 支付时可能需要升级 加速包
         */
        $upgrade_snap_level = $request->input('upgrade_snap_level', 0);
        if ($task_info->isPaidHighLevel()) {
            $upgrade_snap_level = 0;
        }
        Log::info("请求抢票订单支付：{$order_info->getOrderSn()}");
        if ($order_info->isCancel()) {
            return $this->error(1, "订单已取消");
        }

        if (!$order_info->canPay()) {
            return $this->error(1, "无法获取支付信息");
        }

        // 获取支付信息
        $channelRepo = app(ChannelRepositoryInterface::class);
        $pay_info    = $this->orderPayRepo->createTrainOrderPayInfo($order_info, $this->wechatPay,
            $channelRepo->findChannelByAppId($this->appId));


        // 下单时间
        $order_at = $order_info->getCreatedAt();
        // 如果下单时间+ 1h 小于当前时间
        if (Carbon::parse($order_at)->addHour()->lessThan(Carbon::now())) {
            return $this->error(1, "订单超时未支付");
        }


        // 暂时不考虑单独支付
        $out_trade_no = $pay_info->getPaySn();
        $description  = $pay_info->getPayTitle();
        $time_expire  = Carbon::parse($order_at)->addHour()->toRfc3339String();

        /**
         * 构造 attach 返回用于判别 发起支付的 级别 如果是飞机 则飞机可以独立支付
         *
         * @var $attach string type =>trip_type,os =>order_sn
         */
        $attach                   = new PaymentAttach([
            'type'       => PaymentAttach::PAY_TYPE_ORDER,  //下单
            'order_sn'   => $order_info->getOrderSn(),
            'task_id'    => $order_info->getTaskId(),
            'snap_level' => $upgrade_snap_level,
        ]);
        $payData                  = [
            "out_trade_no" => $out_trade_no,
            "description"  => $description,
            "time_expire"  => $time_expire,
            "order_amount" => $pay_info->getPayAmount(),
            "attach"       => $attach,
        ];
        $pay_info                 = $this->get_pay_info($payData);
        $pay_info['order_amount'] = $payData['order_amount'];
        return $this->success('', compact('pay_info'));
    }

    /**
     * @throws InvalidArgumentException
     * @throws Exception
     */
    public function snap_task_high_level_pay_info(Request $request)
    {
        $task_id   = $request->input('task_id');
        $task_info = $this->snapTaskRepository->findOneByTaskId($task_id);
        if ($task_info->getUserId() != auth()->id()) {
            return $this->error(1, "抢票任务不存在");
        }
        // 升级高速后  自动抢票开启
        new SnapTaskAttemptUpgradePaidHighLevel($task_info);
        // 验证是否存在支付
        $pay     = new SnapTaskUpgradePay();
        $has_pay = $pay->findOneByTask($task_info);
        // 如果支付存在 且成功
        if ($has_pay && $has_pay->isPaySuccess()) {
            return $this->error(1, "您已支付,正在为您升级服务中");
        }

        $order_info = $task_info->getTrainOrderInfo();
        $order_at   = $order_info->getCreatedAt();
        $attach     = new PaymentAttach([
            'type'    => PaymentAttach::PAY_TYPE_SNAP_TASK_UPGRADE, // 升级加速包
            'task_id' => $task_info->getTaskId(),
        ]);


        $channelRepo = app(ChannelRepositoryInterface::class);
        $pay_info    = $pay->addPay($task_info, $this->wechatPay, $channelRepo->findChannelByAppId($this->appId));
        $time_expire = Carbon::parse($order_at)->addHour()->toRfc3339String();
        $payData     = [
            "out_trade_no" => $pay_info->getPaySn(),
            "description"  => $pay_info->getPayTitle(),
            "order_amount" => $pay_info->getPayAmount(),
            "attach"       => $attach,
            "time_expire"  => $time_expire,
        ];
        $pay_info    = $this->get_pay_info($payData);
        return $this->success('', compact('pay_info'));
    }

    /**
     * @throws InvalidArgumentException
     * @throws Exception
     */
    public function refund_pay_info(RequiredOrderSnRequest $request)
    {
        $order_info = $this->trainOrderRepo->getOrderInfo($request->input('order_sn'));
        if (!$order_info || $order_info->getUserId() != auth()->id()) {
            return $this->error(404, "订单不存在");
        }

        $train         = $order_info->getTrainInfo();
        $depart_at     = $train->getStartAt();
        $passengerData = OrderSubPassenger::parsePassengerId($request->input('passenger_id'));
        /**
         * @var TrainOrderPassenger $passenger
         */
        $passenger = $order_info->getPassengerBy([
            "passenger_id" => $request->input('passenger_id'),
        ]);
        if (!$passenger) {
            if ($order_info->getOriginOrderSn()) {
                return $this->error(404, "乘客不存在,订单号错误");
            }
            return $this->error(404, "乘客不存在");
        }

        $attach = new PaymentAttach([
            'type'         => PaymentAttach::PAY_TYPE_APPLY_REFUND, // 订单退款需支付差额
            'order_sn'     => $order_info->getOrderSn(),
            'passenger_id' => $passengerData['passenger_id'],
            'sequence'     => $passengerData['sequence'],
        ]);

        $order_passenger_refund = new PiaoChiOrderPassengerRefundImpl($order_info, $passenger);
        if ($order_passenger_refund->getShouldPayAmount() == 0) {
            throw new Exception("不需要支付");
        }
        $channelRepo = app(ChannelRepositoryInterface::class);
        $needPay     = new RefundNeedPay();
        $pay_info    = $needPay->addByTrainOrderPassenger($order_passenger_refund, $this->wechatPay,
            $channelRepo->findChannelByAppId($this->appId));
        // 发车前两小时 不能退票 所以这里 限制 3两小时 5分钟
        $subMinutes        = 185;
        $time_expire       = Carbon::parse($depart_at)->subMinutes($subMinutes)->toRfc3339String();
        $should_pay_amount = $pay_info->getPayAmount();
        $out_trade_no      = $pay_info->getPaySn();
        $payData           = [
            "out_trade_no" => $out_trade_no,
            "description"  => "订单退款需支付差额",
            "order_amount" => $should_pay_amount,
            "attach"       => $attach,
            "time_expire"  => $time_expire,
        ];
        $pay_info          = $this->get_pay_info($payData);
        return $this->success('', compact('pay_info'));
    }


}
